
#include <math/boundingbox.h>
#include <math/ray.h>
#include <math/sphere.h>
#include <gfx/camera.h>
#include <gfx/projector.h>
#include <scene/clipper.h>

/* *******************************************
 *   cePlaneClipper
 * *******************************************/

cePlaneClipper::cePlaneClipper()
  : iClipper ()
{
  CE_OBJECT_CONSTR
}

void cePlaneClipper::AddPlane(const cePlane &plane)
{
  _planes.push_back (plane);
}

void cePlaneClipper::Create(const ceCamera &camera, const ceProjector &projector)
{
  ceVector3f lt (projector.GetLeft(),  projector.GetTop(),    -projector.GetNear());
  ceVector3f rt (projector.GetRight(), projector.GetTop(),    -projector.GetNear());
  ceVector3f lb (projector.GetLeft(),  projector.GetBottom(), -projector.GetNear());
  ceVector3f rb (projector.GetRight(), projector.GetBottom(), -projector.GetNear());

  ceMatrix4f cM = camera.GetMatrix();
  cM.FastInvert();

  const ceVector3f& l = camera.GetEye();

  lt = cM * lt;
  rt = cM * rt;
  lb = cM * lb;
  rb = cM * rb;

  AddPlane(cePlane(l, lt, lb));
  AddPlane(cePlane(l, rt, lt));
  AddPlane(cePlane(l, rb, rt));
  AddPlane(cePlane(l, lb, rb));
}

ceClipResult cePlaneClipper::Test(const ceVector3f &v) const
{

  for (unsigned i=0, j=_planes.size (); i<j; ++i)
    {
      const cePlane& p = _planes[i];
      if (p.Location(v) < 0.0)
        {
          return CR_Out;
        }
    }
  return CR_In;
}

ceClipResult cePlaneClipper::Test(const ceVector3f *vs, unsigned count) const
{
  ceClipResult res = CR_In;

  for (unsigned i=0, j=_planes.size (); i<j; ++i)
    {
      bool allOut = true;
      bool allIn = true;
      const cePlane& p = _planes[i];
      for (unsigned k=0; k<count; k++)
        {
          float d = p.Location(vs[k]);
          if (d >= 0.0f)
            {
              allOut = false;
            }
          else
            {
              allIn = false;
            }
        }
      if (allOut)
        {
          return CR_Out;
        }
      if (!allIn)
        {
          res = CR_Intermediate;
        }
    }
  return res;
}

ceClipResult cePlaneClipper::Test(const ceBoundingBox &bbox) const
{
  return Test (bbox.GetPoints(), 8);
}


ceClipResult cePlaneClipper::Test (const ceSphere& sphere) const
{
	ceClipResult res = CR_In;

  for (unsigned i=0, j=_planes.size (); i<j; ++i)
    {
      const cePlane& p = _planes[i];

			float d = p.Location (sphere.GetCenter ());
			if (d <= -sphere.GetRadius ())
				{
					// outside one single plane means the sphere is completly outside of
					// the clipper
					return CR_Out;
				}
			else if (d < sphere.GetRadius ())
				{
					// it is not outside but not completly inside
					// so the result is atleast intermediat
					res = CR_Intermediate;
				}
    }
  return res;
}

/* *******************************************
 *   ceRayClipper
 * *******************************************/

ceRayClipper::ceRayClipper ()
  : iClipper ()
{
  CE_OBJECT_CONSTR
}

void ceRayClipper::AddRay(const ceRay &ray)
{
  _rays.push_back(ray);
}

ceClipResult ceRayClipper::Test(const ceVector3f &v) const
{
  for (unsigned i=0, j=_rays.size(); i<j; ++i)
    {
      const ceRay& ray = _rays[i];

      // get the position best fits on the ray
      float posOnRay = ray.GetLocation(v);

      // get the real position
      ceVector3f realPos = ray.GetLocation(posOnRay);

      float dist = (realPos - v).Length();
      if (dist < _perPointDistance)
        {
          return CR_Intermediate;
        }
    }
  return CR_Out;
}


ceClipResult ceRayClipper::Test(const ceVector3f *vs, unsigned count) const
{
  if (count < 3)
    {
      return CR_Out;
    }

  for (unsigned i=0, j=_rays.size(); i<j; ++i)
    {
      const ceRay& ray = _rays[i];
      for (unsigned k=1; k<count-1; ++k)
        {

        }
    }
  return CR_Intermediate;
}

ceClipResult ceRayClipper::Test(const ceBoundingBox &bbox) const
{
  return CR_Intermediate;
}

ceClipResult ceRayClipper::Test (const ceSphere &sphere) const
{
  return CR_Intermediate;
}
